﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using Dotnet.Utils.Utility.ObjectExtensions.To;

namespace Dotnet.Utils.Utility.ObjectExtensions
{
    public static class ReflectionUtil
    {
        public static string GetDescription<T>()
        {
            return GetDescription(typeof(T));
        }

        public static string GetDescription<T>(string memberName)
        {
            return GetDescription(typeof(T), memberName);
        }

        public static string GetDescription(Type type, string memberName)
        {
            string result;
            if (type == null)
            {
                result = string.Empty;
            }
            else if (string.IsNullOrWhiteSpace(memberName))
            {
                result = string.Empty;
            }
            else
            {
                result = GetDescription(type.GetTypeInfo().GetMember(memberName).FirstOrDefault());
            }
            return result;
        }

        public static string GetDescription(MemberInfo member)
        {
            string result;
            if (member == null)
            {
                result = string.Empty;
            }
            else
            {
                DescriptionAttribute customAttribute = member.GetCustomAttribute<DescriptionAttribute>();
                result = customAttribute != null ? customAttribute.Description : member.Name;
            }
            return result;
        }

        public static string GetDisplayName<T>()
        {
            return GetDisplayName(typeof(T));
        }

        public static string GetDisplayName(MemberInfo member)
        {
            string result;
            if (member == null)
            {
                result = string.Empty;
            }
            else
            {
                DisplayNameAttribute customAttribute = member.GetCustomAttribute<DisplayNameAttribute>();
                if (customAttribute != null)
                {
                    result = customAttribute.DisplayName;
                }
                else
                {
                    result = string.Empty;
                }
            }
            return result;
        }

        public static string GetDisplayNameOrDescription<T>()
        {
            return GetDisplayNameOrDescription(typeof(T));
        }

        public static string GetDisplayNameOrDescription(MemberInfo member)
        {
            string displayName = GetDisplayName(member);
            return string.IsNullOrWhiteSpace(displayName) ? GetDescription(member) : displayName;
        }

        public static object CreateInstance(Type type, params object[] parameters)
        {
            object result;
            if (parameters != null)
            {
                result = Activator.CreateInstance(type, parameters);
            }
            else
            {
                result = Activator.CreateInstance(type);
            }
            return result;
        }

        public static T CreateInstance<T>(Type type, params object[] parameters)
        {
            return ConvertUtil.As<T>(CreateInstance(type, parameters));
        }

        public static bool IsBool(MemberInfo member)
        {
            bool result;
            if (member == null)
            {
                result = false;
            }
            else
            {
                MemberTypes memberType = member.MemberType;
                if (memberType != MemberTypes.Property)
                {
                    result = memberType == MemberTypes.TypeInfo && member.ToString() == "System.Boolean";
                }
                else
                {
                    result = IsBool((PropertyInfo)member);
                }
            }
            return result;
        }

        private static bool IsBool(PropertyInfo property)
        {
            return property.PropertyType == typeof(bool) || property.PropertyType == typeof(bool?);
        }

        public static bool IsEnum(MemberInfo member)
        {
            bool result;
            if (member == null)
            {
                result = false;
            }
            else
            {
                MemberTypes memberType = member.MemberType;
                if (memberType != MemberTypes.Property)
                {
                    result = memberType == MemberTypes.TypeInfo && ((TypeInfo)member).IsEnum;
                }
                else
                {
                    result = IsEnum((PropertyInfo)member);
                }
            }
            return result;
        }

        private static bool IsEnum(PropertyInfo property)
        {
            bool result;
            if (property.PropertyType.GetTypeInfo().IsEnum)
            {
                result = true;
            }
            else
            {
                Type underlyingType = Nullable.GetUnderlyingType(property.PropertyType);
                result = !(underlyingType == null) && underlyingType.GetTypeInfo().IsEnum;
            }
            return result;
        }

        public static bool IsDate(MemberInfo member)
        {
            bool result;
            if (member == null)
            {
                result = false;
            }
            else
            {
                MemberTypes memberType = member.MemberType;
                if (memberType != MemberTypes.Property)
                {
                    result = memberType == MemberTypes.TypeInfo && member.ToString() == "System.DateTime";
                }
                else
                {
                    result = IsDate((PropertyInfo)member);
                }
            }
            return result;
        }

        private static bool IsDate(PropertyInfo property)
        {
            return property.PropertyType == typeof(DateTime) || property.PropertyType == typeof(DateTime?);
        }

        public static bool IsInt(MemberInfo member)
        {
            bool result;
            if (member == null)
            {
                result = false;
            }
            else
            {
                MemberTypes memberType = member.MemberType;
                if (memberType != MemberTypes.Property)
                {
                    result = memberType == MemberTypes.TypeInfo && (member.ToString() == "System.Int32" || member.ToString() == "System.Int16" || member.ToString() == "System.Int64");
                }
                else
                {
                    result = IsInt((PropertyInfo)member);
                }
            }
            return result;
        }

        private static bool IsInt(PropertyInfo property)
        {
            return property.PropertyType == typeof(int) || property.PropertyType == typeof(int?) || property.PropertyType == typeof(short) || property.PropertyType == typeof(short?) || property.PropertyType == typeof(long) || property.PropertyType == typeof(long?);
        }

        public static bool IsNumber(MemberInfo member)
        {
            bool result;
            if (member == null)
            {
                result = false;
            }
            else if (IsInt(member))
            {
                result = true;
            }
            else
            {
                MemberTypes memberType = member.MemberType;
                if (memberType != MemberTypes.Property)
                {
                    result = memberType == MemberTypes.TypeInfo && (member.ToString() == "System.Double" || member.ToString() == "System.Decimal" || member.ToString() == "System.Single");
                }
                else
                {
                    result = IsNumber((PropertyInfo)member);
                }
            }
            return result;
        }

        private static bool IsNumber(PropertyInfo property)
        {
            return property.PropertyType == typeof(double) || property.PropertyType == typeof(double?) || property.PropertyType == typeof(decimal) || property.PropertyType == typeof(decimal?) || property.PropertyType == typeof(float) || property.PropertyType == typeof(float?);
        }

        public static bool IsCollection(Type type)
        {
            return type.IsArray || IsGenericCollection(type);
        }

        public static bool IsGenericCollection(Type type)
        {
            bool result;
            if (!type.IsGenericType)
            {
                result = false;
            }
            else
            {
                Type genericTypeDefinition = type.GetGenericTypeDefinition();
                result = genericTypeDefinition == typeof(IEnumerable<>) || genericTypeDefinition == typeof(IReadOnlyCollection<>) || genericTypeDefinition == typeof(IReadOnlyList<>) || genericTypeDefinition == typeof(ICollection<>) || genericTypeDefinition == typeof(IList<>) || genericTypeDefinition == typeof(List<>);
            }
            return result;
        }

        public static Type GetTopBaseType<T>()
        {
            return GetTopBaseType(typeof(T));
        }

        public static Type GetTopBaseType(Type type)
        {
            Type result;
            if (type == null)
            {
                result = null;
            }
            else if (type.IsInterface)
            {
                result = type;
            }
            else if (type.BaseType == typeof(object))
            {
                result = type;
            }
            else
            {
                result = GetTopBaseType(type.BaseType);
            }
            return result;
        }
    }
}